/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

#include "Types.h"

#include "TypeLibrary.h"

extern void type_library_init( type_library * library )
{
	printf("\n");
	printf("Argot Version 1.2.0\n" );
	printf("Copyright 2003-2005 (C) Live Media Pty Ltd\n");
	printf("www.einet.com.au\n" );
	
	intmap_init( &library->types, 4 );
	intmap_init( &library->names, 4 );
	library->count = 0;
	library->error = TYPE_OK;
}


int type_library_get_error( type_library * library )
{
	return library->error;
}

void type_library_set_error( type_library * library, int error )
{
	library->error = error;
}


static int type_library_add( type_library * library, type_definition * definition )
{
	int id = library->count++;
	
	intmap_add( &library->types, id, (int) definition, NULL );
	definition->id = id;
	intmap_add( &library->names, definition->name->hash, (int) definition, NULL );
	return id;
}

int hashName( char * str )
{
	register int hash = 5381;
	int c;
	
	while ( (c = *str++) != 0 )
	    hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
	return hash; 
}

static void lowerCase( char * s )
{
	int x=0;
	while ( s[x] != 0 )
	{
		/* FIXME why is this failing? */
		/* s[x] = tolower( s[x] ); */
		x++;
	}
}

static bool checkName( type_name * name )
{
    if ( name == NULL || name->name == NULL )
        return false;
    
    if ( strlen( name->name ) == 0 )
    	return false;
  
    if ( name->hash == 0 )
    {
    	lowerCase( name->name );
    	name->hash = hashName( name->name );
    }
    return true;
}

void type_library_destroy( type_library * library )
{
	intmap_destroy( &library->names );
	intmap_destroy( &library->types );
}

#define find_library_name( library, name ) intmap_find( &((library)->names), (name)->hash )==INTMAP_OK?((type_definition *) intmap_value( &library->names )):NULL
#define find_library_id( library, id ) intmap_find( &((library)->types), id )==INTMAP_OK?((type_definition *) intmap_value( &library->types )):NULL

int type_library_get_state( type_library * library, type_name * name )
{
	type_definition * definition;
	
    if ( !checkName( name ) )
        return TYPE_NOT_DEFINED;
    
    definition = (type_definition *) find_library_name( library, name );
    if ( definition == NULL )
        return TYPE_NOT_DEFINED;
    return definition->state;
}

int type_library_get_id_type_state( type_library * library, int id )
{
    type_definition * definition = (type_definition *) find_library_id( library, id );
    if ( definition == NULL )
        return TYPE_NOT_DEFINED;
    return definition->state;
}

int type_library_register_bind( type_library * library, type_name * name, type_element * structure, typeReader reader, void * readerData, typeWriter writer, void * writerData )
{
	int state;
	type_definition * definition;
	
	
    if ( !checkName( name ) )
    	return TYPE_NAME_INVALID;
    
    if ( structure == NULL )
        return TYPE_INVALID_PARAM;
    if ( reader == NULL )
        return TYPE_INVALID_PARAM;
    if ( writer == NULL )
        return TYPE_INVALID_PARAM;
        	    
    state = type_library_get_state( library, name );
    if ( state != TYPE_NOT_DEFINED && state != TYPE_RESERVED )
    {
        return TYPE_BAD_STATE;
    }
        
    if ( state == TYPE_NOT_DEFINED )
    {
	    definition = (type_definition *) type_malloc( sizeof( type_definition ) );
	    definition->library = library;
	    definition->name = name;
	    definition->structure = structure;
	    definition->reader.reader = reader;
	    definition->reader.data = readerData;
	    definition->writer.writer = writer;
	    definition->writer.data = writerData;
	    definition->state = TYPE_COMPLETE;
	    	    
	    type_library_add( library, definition );
    }
    else if ( state == TYPE_RESERVED )
    {
    	definition = (type_definition *) find_library_name( library, name );
    	if ( definition == NULL )
    		return TYPE_BAD_STATE;

	    definition->structure = structure;
	    definition->reader.reader = reader;
	    definition->reader.data = readerData;
	    definition->writer.writer = writer;
	    definition->writer.data = writerData;
	    definition->state = TYPE_COMPLETE;

    }
    else
    {
        return TYPE_INVALID_STATE;
    }
    
    state = structure->bind( structure, definition );
    if ( state != TYPE_OK )
    	return state;

    return definition->id;
}

int type_library_register( type_library * library, type_name * name, type_element * structure )
{
	int state;
	type_definition * definition;
	
	if ( !checkName( name ) )
		return TYPE_BAD_NAME;
	    
    if ( structure == NULL )
        return TYPE_INVALID_PARAM;
	    
	state = type_library_get_state( library, name );
	if ( state != TYPE_NOT_DEFINED && state != TYPE_RESERVED  )
	{
		return TYPE_DEFINED;
	}
		    
    if ( state == TYPE_NOT_DEFINED )
    {
	    definition = (type_definition *) type_malloc( sizeof( type_definition ) );
	    definition->library = library;
	    definition->name = name;
	    definition->structure = structure;
	    definition->reader.reader = NULL;
	    definition->reader.data = NULL;
	    definition->writer.writer = NULL;
	    definition->writer.data = NULL;
	    definition->state = TYPE_REGISTERED;
	    	    
	    type_library_add( library, definition );		    
    }
	else if ( state == TYPE_RESERVED )
    {
    	definition = (type_definition *) find_library_name( library, name );
    	if ( definition == NULL )
    		return TYPE_BAD_STATE;

	    definition->structure = structure;
	    definition->state = TYPE_REGISTERED;
    }
    else
    {
        return TYPE_INVALID_STATE;
    }
	
	state = structure->bind( structure, definition );
	if ( state != TYPE_OK )
		return state;
	    
	return definition->id;
}

int type_library_reserve( type_library * library, type_name * name )
{
	type_definition * definition;

    if ( !checkName( name ) )
    	return TYPE_INVALID_PARAM;
    
    if ( type_library_get_state( library, name ) != TYPE_NOT_DEFINED )
    {
        return TYPE_NAME_DEFINED;
    }
    
    definition = (type_definition *) type_malloc( sizeof( type_definition ) );
    definition->library = library;
    definition->name = name;
    definition->structure = NULL;
    definition->reader.reader = NULL;
    definition->reader.data = NULL;
    definition->writer.writer = NULL;
    definition->writer.data = NULL;
    definition->state = TYPE_RESERVED;
    	    
    return type_library_add( library, definition );
}

int type_library_bind( type_library * library, type_name * name, typeReader reader, void * readerData, typeWriter writer, void * writerData )
{
    type_definition * definition;
    
	if ( !checkName( name ) )
		return TYPE_INVALID_PARAM;
	
	definition = (type_definition *) find_library_name( library, name );
	if ( definition == NULL )
		return TYPE_NOT_DEFINED;
	
    if ( definition->state != TYPE_REGISTERED )
        return TYPE_INVALID_STATE;
    
    definition->reader.reader = reader;
    definition->reader.data = readerData;
    definition->writer.writer = writer;
    definition->writer.data = writerData;
    definition->state = TYPE_COMPLETE;
    
    return definition->id;
}

int type_library_get_id( type_library * library, type_name * name )
{
	type_definition * definition;
	
	if ( !checkName( name ) )
		return TYPE_INVALID_PARAM;

	definition = (type_definition *) find_library_name( library, name );
	if ( definition == NULL )
		return TYPE_NOT_DEFINED;
		
	return definition->id;
}

type_name * type_library_get_name( type_library * library, int id )
{
	type_definition * definition = (type_definition *) find_library_id( library, id );
	if ( definition == NULL )
	{
		type_library_set_error( library, TYPE_NOT_DEFINED );
		return NULL;
	}
	
	return definition->name;
}

type_element * type_library_get_structure( type_library * library, int id )
{
	type_definition * definition = (type_definition *) find_library_id( library, id );
	if ( definition == NULL )
	{
		type_library_set_error( library, TYPE_NOT_DEFINED );
		return NULL;
	}
	
	if ( definition->state != TYPE_COMPLETE && definition->state != TYPE_REGISTERED )
	{
		type_library_set_error( library, TYPE_NOT_COMPLETE );
		return NULL;
	}

	return definition->structure;
}

type_definition * type_library_get_definition( type_library * library, type_name * name )
{
	type_definition * definition;
	
	if ( !checkName( name ) )
	{
		type_library_set_error( library, TYPE_INVALID_PARAM );
		return NULL;
	}

	definition = (type_definition *) find_library_name( library, name );
	if ( definition == NULL )
	{
		type_library_set_error( library, TYPE_NOT_DEFINED );
		return NULL;
	}
		
	return definition;
}

type_definition * type_library_get_definition_id( type_library * library, int id )
{
	type_definition * definition;
	
	definition = (type_definition *) find_library_id( library, id );
	if ( definition == NULL )
	{
		type_library_set_error( library, TYPE_NOT_DEFINED );
		return NULL;
	}
		
	return definition;
}

type_reader * type_library_get_reader( type_library * library, int id )
{
	type_definition * definition = (type_definition *) find_library_id( library, id );
	if ( definition == NULL )
	{
		type_library_set_error( library, TYPE_NOT_DEFINED );
		return NULL;
	}
	
	if ( definition->state != TYPE_COMPLETE )
	{
		type_library_set_error( library, TYPE_NOT_COMPLETE );
		return NULL;
	}
		
	return &(definition->reader);
}

type_writer * type_library_get_writer( type_library * library, int id )
{
    type_definition * definition = (type_definition *) find_library_id( library, id );
	if ( definition == NULL )
	{
		type_library_set_error( library, TYPE_NOT_DEFINED );
		return NULL;
	}
	
	if ( definition->state != TYPE_COMPLETE )
	{
		type_library_set_error( library, TYPE_NOT_COMPLETE );
		return NULL;
	}
	
	return &(definition->writer);
}

